#include "device.h"
#include "component.h"
#include <stdio.h>
#include <stdlib.h>
#include "math.h"

//#define PIR_TEST_WITHOUT_INTERVAL
//#define PIR_DEBUG

/* 调试打印接口 */
#define PIR_LOG(format, ...) OSAL_LOG(C_LIGHT_PURPLE format C_NONE, ##__VA_ARGS__)
#define __PIR_LOG(format, ...) //__OSAL_LOG(C_LIGHT_PURPLE format C_NONE, ##__VA_ARGS__)

/* 虚拟硬件接口 */
#define VHW_ADC_PIR vADC_4
#define VHW_IRQ_PIR vPIN_I18

#define EVENT_PIR_PRO                       (0x00000001)
#define EVENT_PIR_READ_ADC                  (0x00000002)
#define EVENT_PIR_TEST                      (0x00000004)
#define EVENT_PIR_FAULT_DETECT              (0x00000008)


#define PIR_TIMEOUT_SEC_TIME                    5*60   //5分钟，每次触发徘徊报警后下一次开始检测的间隔
#define PIR_PARAM_WAKE_INTERVAL                 50     //50ms ，睡眠下唤醒时间，pir adc采样频率
#define PIR_FAULT_INTERVAL                      60     //60ms ，故障检测，pir adc采样频率

//PIR 无人体检测时adc值的范围
#define PIR_ADC_BASE                          1650
#define PIR_ADC_BASE_MAX                      1700        
#define PIR_ADC_BASE_MIN                      1600

//adc的最小值
#define PIR_SENS_L_FLOOR                    750        //1M  下限
#define PIR_SENS_M_FLOOR                    1000        //2M
#define PIR_SENS_H_FLOOR                    1500         //3M

//PIR灵敏度 adc最大值和最小值的差值
#define PIR_SENS_L_DIF_THR                  1500        //1M  差值阀值
#define PIR_SENS_M_DIF_THR                  700         //2M     
#define PIR_SENS_H_DIF_THR                  350         //3M
 

static struct 
{
    uint8_t init_flag;          //初始化标志
    uint8_t enable;             //pir 开关使能
    uint8_t wander_num;         //徘徊次数
    uint16_t floor;             //pir adc灵敏度下限值
    uint16_t dif_value;         //pir 差值
    uint32_t wander_time;       //徘徊时间
}pir_info;

static uint16_t voltage_max = 0;    //当前adc的最大值
static uint16_t voltage_min = 0;    //当前adc的最小值
static uint8_t irq_wakeup_cnt = 0;  //唤醒过滤计数
static uint8_t wander_cnt = 0;       //徘徊计数
static uint32_t adc_detec_cnt = 0;      //adc采集次数
static uint8_t pir_running = 0;     //启动pir处理
static uint32_t timeout_timestamp = PIR_TIMEOUT_SEC_TIME;  //超时时间戳
static uint32_t pir_wakeup_interval = KOS_PARAM_WAKE_INTERVAL;  //pir唤醒间隙


static uint8_t p_err_flag = 0;      //故障码标志位


/**
  * @brief  故障处理
  * @note
  * @param  err：是否故障
  * @return void
  */
static void err_process(uint8_t err)
{
	static uint8_t err_flag = 0xff;
    if (err != err_flag)
    {
        err_flag = err;
        OSAL_MessagePublishErrorCode(ERRCODE_TYPE_PIRSENSOR, err_flag);
    }
}

/**
 * @brief: 读取PIR的电压值
 * @note:
 * @param {*}
 * @return {*}
 */
static uint16_t PIR_GetVoltage(void)
{
    uint16_t voltage = (uint16_t)Device_Read(VHW_ADC_PIR, NULL, 0, 0);
    return voltage; 
}

/**
 * @brief: 读PIR中断IO的电平
 * @note: 
 * @param {*}
 * @return {*}
 */
static bool PIR_ReadIRQ()
{
    if (Device_Read(VHW_IRQ_PIR,NULL,0,0) == 0)
    {
        return false;
    }    
    else 
    {
        return true;
    }
}

/**
  * @brief  INT引脚中断回调
  *         
  * @note   在中断里面执行的，这里一般设置ISR事件即可，不要做过多的逻辑
  */
static void Pir_IrqHandle(VirtualHardware_enum_t dev, void *data, uint32_t len)
{
    
}

/**
  * @brief  PIR故障检测
  *         
  * @note   
  */
void Pir_Faultdetect(void)
{
    uint16_t voltage = (uint16_t)Device_Read(VHW_ADC_PIR, NULL, 0, 0);
    uint8_t pir_irq = PIR_ReadIRQ();
    static uint32_t cnt = 0;
    static uint32_t time_cnt = 0;
    // PIR_LOG("Pir_Faultdetect voltage:%4d,pir_irq:%4d\r\n",voltage,pir_irq);

    time_cnt++;
    if((voltage < (PIR_ADC_BASE_MIN - 100)) && (pir_irq == 0))
    {
        cnt++;
    }
    else
    {
        if(cnt > 0)
        {
            cnt--;
        }
    }

    //80*60ms 检测5s
    if(time_cnt > 80)
    {
        if(cnt > 75)
        {
            PIR_LOG("Pir err\r\n");
            err_process(1);
            p_err_flag = 1;
            
        }
        else
        {
            PIR_LOG("Pir not err\r\n");
            err_process(0);
            p_err_flag = 0;
        }
        time_cnt = 0;
        cnt = 0;

        OSAL_EventDelete(COMP_PIR, EVENT_PIR_FAULT_DETECT);
    }


}

/**
  * @brief  清除变量
  *         
  * @note   
  */
static void Pir_ClearValue(void)
{
    voltage_max = PIR_ADC_BASE_MAX;
    voltage_min = PIR_ADC_BASE_MIN;
    adc_detec_cnt = 0;
    wander_cnt = 0;
    if(pir_running != 2)
    {
        pir_running = 0;
    }

}

/**
  * @brief  读adc处理
  *         
  * @note   
  */
void Pir_ReadAdcProcess(void)
{
    uint32_t cur_time = 0;
    uint32_t period_time = 0;
    uint16_t voltage = (uint16_t)Device_Read(VHW_ADC_PIR, NULL, 0, 0);
    uint8_t pir_irq = PIR_ReadIRQ();
    
    adc_detec_cnt++;
    cur_time = adc_detec_cnt * PIR_PARAM_WAKE_INTERVAL;

    if(voltage > voltage_max)
    {
        voltage_max = voltage;
    }

    if(voltage < voltage_min)
    {
        voltage_min = voltage;
    }

    //差值满足在范围内
    if(((voltage_max - voltage_min) >= pir_info.dif_value ) &&  (voltage_max != PIR_ADC_BASE_MAX))
    {
        pir_running = 1;
        wander_cnt++;
        PIR_LOG("Pir_ISRProcess max:%4d,min:%4d\r\n",voltage_max,voltage_min);
        voltage_max = PIR_ADC_BASE_MAX;
        voltage_min = PIR_ADC_BASE_MIN;

        period_time = pir_info.wander_time - (pir_info.wander_time / pir_info.wander_num);
        //当前时间大于最后时间段的起点，
        if((cur_time >= period_time) && (cur_time <= pir_info.wander_time))
        {
            pir_running = 2;
        }
    }

    //满足条件 徘徊报警触发
    if(pir_running == 2)      
    {

        Pir_ClearValue();
        #ifdef PIR_TEST_WITHOUT_INTERVAL
        timeout_timestamp = PIR_TIMEOUT_SEC_TIME;
        #else
        OSAL_TimeGet(&timeout_timestamp, T_UTC);        //记录当前时间戳
        #endif 
        
        PIR_LOG("Pir Trigger hovering alarm");
        OSAL_EventSingleCreate(COMP_PIR,EVENT_PIR_PRO,PIR_PARAM_WAKE_INTERVAL,EVT_PRIORITY_MEDIUM);
    }
    else if((cur_time >= pir_info.wander_time) && (pir_running != 2))    //检测时间已到，不满足触发
    {
        PIR_LOG("Pir Clear");
        Pir_ClearValue();
    }


}

/**
  * @brief  产测，检测pir感应
  *         
  * @note   
  */
static void Pir_TestProcess(void)
{
    uint16_t voltage = (uint16_t)Device_Read(VHW_ADC_PIR, NULL, 0, 0);
    uint8_t pir_irq = PIR_ReadIRQ();
    static uint16_t test_voltage_max = PIR_ADC_BASE_MAX;
    static uint16_t test_voltage_min = PIR_ADC_BASE_MIN;
    static uint8_t cnt = 0;

    PIR_LOG("Pir_ateISRProcess max:%4d,min:%4d\r\n",test_voltage_max,test_voltage_min);

    if(voltage > test_voltage_max)
    {
        test_voltage_max = voltage;
    }
    if(voltage < test_voltage_min)
    {
        test_voltage_min = voltage;
    }

    //差值满足在范围内
    if(((test_voltage_max - test_voltage_min) >= PIR_SENS_H_DIF_THR ) && (test_voltage_max != PIR_ADC_BASE_MAX))
    {
        cnt++;
        test_voltage_max = PIR_ADC_BASE_MAX;
        test_voltage_min = PIR_ADC_BASE_MIN;
    }

    if(cnt >= 1)
    {
        cnt = 0;
        PirMsg_t msg;
        msg.alarm = 2;
        OSAL_MessagePublish(&msg, sizeof(msg));

        PIR_LOG("Pir Ate publish \r\n");
        OSAL_EventDelete(COMP_PIR, EVENT_PIR_TEST);
    }


}

/**
 * @brief: PIR 处理徘徊报警上报
 * @note:
 * @return 
 */
static void Pir_WanderProcess(void)
{
    PirMsg_t msg;
    msg.alarm = 1;
    OSAL_MessagePublish(&msg, sizeof(msg));
    PIR_LOG("Pir publish hovering alarm\r\n............................\r\n");
    pir_running = 0;
}



/**
 * @brief: PIR 设置灵敏度
 * @note:
 * @param level：灵敏度等级( 高，中，低)
 * @return 
 */
static uint32_t Pir_SetSentivity(PirSens_enum_t level)
{
    switch (level)
    {
    case PIR_SENS_H:
        pir_info.floor = PIR_SENS_H_FLOOR;
        pir_info.dif_value = PIR_SENS_H_DIF_THR;
        break;

    case PIR_SENS_M:
        pir_info.floor = PIR_SENS_M_FLOOR;
        pir_info.dif_value = PIR_SENS_M_DIF_THR;
        break;
    
    case PIR_SENS_L:
        pir_info.floor = PIR_SENS_L_FLOOR;
        pir_info.dif_value = PIR_SENS_L_DIF_THR;
        break;

    default:
        break;
    }
    OSAL_NvWrite(0, &pir_info, sizeof(pir_info));
    Pir_ClearValue();
    timeout_timestamp = 0;
    PIR_LOG("Pir set sentivity %d\r\n",level);

    return 0;
}


/**
 * @brief: PIR 设置徘徊时间
 * @note:
 * @param time：时间(ms)
 * @return 
 */
static uint32_t Pir_SetWanderTime(uint32_t time)
{
    pir_info.wander_time = time;
    switch (pir_info.wander_time)
    {
    case 10000:
        pir_info.wander_num = 3;
        break;

    case 20000:
        pir_info.wander_num = 4;
        break;

    case 30000:
        pir_info.wander_num = 5;
        break;

    case 60000:
        pir_info.wander_num = 8;
        break;
    
    default:
        break;
    }
    OSAL_NvWrite(0, &pir_info, sizeof(pir_info));
    Pir_ClearValue();
    timeout_timestamp = 0;

    PIR_LOG("Pir set wandertime:%d, num:%d\r\n",pir_info.wander_time,pir_info.wander_num);

    return 0;
}


/**
 * @brief: PIR 开关
 * @note:
 * @param data:使能，0：关闭，1：打开
 * @return 
 */
static uint32_t Pir_Switch(uint8_t data)
{
    pir_info.enable = data;
    OSAL_NvWrite(0, &pir_info, sizeof(pir_info));

    Pir_ClearValue();
    PIR_LOG("Pir switch %d\r\n",pir_info.enable);
    if(pir_info.enable)
    {
        //打开pir
        timeout_timestamp = 0;
        OSAL_EventRepeatCreate(COMP_PIR,EVENT_PIR_READ_ADC,PIR_PARAM_WAKE_INTERVAL,EVT_PRIORITY_MEDIUM);  
    }
    else
    {
        //关闭pir
        OSAL_EventDelete(COMP_PIR, EVENT_PIR_READ_ADC);
        pir_wakeup_interval = KOS_PARAM_WAKE_INTERVAL;
        OSAL_SetWakeupInterval(pir_wakeup_interval);
    }
}

static uint32_t Pir_AteCheck(void)
{
    OSAL_EventRepeatCreate(COMP_PIR,EVENT_PIR_TEST,PIR_PARAM_WAKE_INTERVAL,EVT_PRIORITY_MEDIUM);
}

/**
 * @brief: PIR 初始化
 * @note:
 * @param 
 */
static void Pir_Init(void)
{

    static uint8_t init_flag = 0;

    if(init_flag == 0)
    {
        init_flag = 1;
        OSAL_NvRead(0, &pir_info, sizeof(pir_info));

        if (pir_info.init_flag != 0xFA)
        {
            pir_info.init_flag = 0xFA;
            pir_info.floor = PIR_SENS_M_FLOOR;
            pir_info.dif_value = PIR_SENS_M_DIF_THR;
            pir_info.wander_time = 10*1000;  //默认10s
            pir_info.wander_num = 3;
            pir_info.enable = 0;
            OSAL_NvWrite(0, &pir_info, sizeof(pir_info));
            OSAL_LOG("PIR task info init");
        }

        
    }
    //启动故障检测
    OSAL_EventRepeatCreate(COMP_PIR,EVENT_PIR_FAULT_DETECT,PIR_FAULT_INTERVAL,EVT_PRIORITY_MEDIUM);

    voltage_max = PIR_ADC_BASE_MAX;
    voltage_min = PIR_ADC_BASE_MIN;

    if(pir_info.enable)
    {
        OSAL_EventRepeatCreate(COMP_PIR,EVENT_PIR_READ_ADC,PIR_PARAM_WAKE_INTERVAL,EVT_PRIORITY_MEDIUM);  
    }


}



/**
 * @brief: PIR检测任务
 * @note:
 * @param {uint32_t} event
 * @return {*}
 */
static uint32_t Pir_Task(uint32_t event)
{

    /* 系统启动事件 */
    if (event & EVENT_SYS_START)
    {
        Pir_Init();
        PIR_LOG("PIR task start");
        Device_Enable(VHW_ADC_PIR);
        Device_Disable(VHW_IRQ_PIR);
        Device_RegisteredCB(VHW_IRQ_PIR, Pir_IrqHandle);
        Device_ConfigCB(VHW_IRQ_PIR, ENABLE);
        // OSAL_SetTaskStatus(TASK_STA_ACTIVE);

        //唤醒触发徘徊报警
        if(pir_running == 2) 
        {
            pir_running = 0;
            wander_cnt = 0;
            OSAL_EventSingleCreate(COMP_PIR,EVENT_PIR_PRO,PIR_PARAM_WAKE_INTERVAL,EVT_PRIORITY_MEDIUM); 
        } 

        SYS_API(Pir_SetSentivity);
        SYS_API(Pir_SetWanderTime);
        SYS_API(Pir_Switch);
        SYS_API(Pir_AteCheck);

        return (event ^ EVENT_SYS_START);
    }

    if(event & EVENT_PIR_READ_ADC)
    {
        uint32_t cur_rtc = 0;
        OSAL_TimeGet(&cur_rtc, T_UTC);
        // timeout_timestamp += PIR_PARAM_WAKE_INTERVAL;
        if((cur_rtc - timeout_timestamp) >= PIR_TIMEOUT_SEC_TIME)
        {
            Pir_ReadAdcProcess();
        }
        
        return (event ^ EVENT_PIR_READ_ADC);
    }

    if(event & EVENT_PIR_TEST)
    {
        Pir_TestProcess();
        return (event ^ EVENT_PIR_TEST);
    }

    if(event & EVENT_PIR_PRO)
    {
        Pir_WanderProcess();
        return (event ^ EVENT_PIR_PRO);
    }

    if(event & EVENT_PIR_FAULT_DETECT)
    {
        Pir_Faultdetect();
        return (event ^EVENT_PIR_FAULT_DETECT);
    }

    /* 系统休眠事件 */
    if (event & EVENT_SYS_SLEEP)
    {
        PIR_LOG("PIR task sleep\r\n");
        PIR_LOG("timeout_timestamp :%d",timeout_timestamp);
        PIR_LOG("current pir level, adc_floor:%d, dif_value:%d",pir_info.floor,pir_info.dif_value);
        voltage_max = PIR_ADC_BASE_MAX;
        voltage_min = PIR_ADC_BASE_MIN;
        OSAL_EventDelete(COMP_PIR, EVENT_PIR_READ_ADC);
        Device_ConfigCB(VHW_IRQ_PIR, DISABLE);
        if(pir_running && pir_info.enable)
        {
            pir_wakeup_interval = PIR_PARAM_WAKE_INTERVAL;
            OSAL_SetWakeupInterval(pir_wakeup_interval);
        }
        if(p_err_flag)
        {
            //pir没有接，io口配置下拉
            PIR_LOG("pir disable\r\n");
            Device_Disable(VHW_IRQ_PIR);
            Device_Disable(VHW_ADC_PIR);    //ADC配置下拉
        }
        else
        {
            Device_Enable(VHW_IRQ_PIR);
        }
        return (event ^ EVENT_SYS_SLEEP);
    }

    return 0;
}
COMPONENT_TASK_EXPORT(COMP_PIR, Pir_Task, 16);


/**
  * @brief  唤醒回调
  *         
  * @note   休眠状态下INT触发：会执行这个CB
  * @return 返回0：不唤醒， 返回非0：唤醒
  */
static int32_t Pir_WakeHandle(uint32_t dev)
{
    // printf("pir irq wakeup\r\n");
    // if(irq_wakeup_cnt > 0)
    // {
    //     irq_wakeup_cnt--;
    // }

    return 0;
}
COMPONENT_WAKEUP_EXPORT(COMP_PIR, Pir_WakeHandle, VHW_IRQ_PIR);


static int32_t Pir_TimeWakeupProcess(void)
{
    static uint8_t adc_flag = 0;
    uint32_t cur_time = 0;              //当前时间
    uint32_t period_time = 0;
    uint16_t voltage = PIR_GetVoltage();

    if(pir_running == 0)
    {
        if (PIR_ReadIRQ() == true )//
        { 
            // printf("pir irq time wakeup\r\n");

            if(abs(voltage - PIR_ADC_BASE) >= 250)
            {
                adc_flag = 1;
                printf("pir_base \r\n");
            }
            irq_wakeup_cnt++;
        }
        else
        {
            adc_flag = 0;
            irq_wakeup_cnt = 0;
        }

    }
    else
    {
        adc_detec_cnt++;
        cur_time = adc_detec_cnt * PIR_PARAM_WAKE_INTERVAL;

        if(voltage > voltage_max)
        {
            voltage_max = voltage;
        }
        else if(voltage < voltage_min)
        {
            voltage_min = voltage;
        }

        if(((voltage_max - voltage_min) >= pir_info.dif_value))
        {
            wander_cnt++;
            // printf("Pir_ISRProcess max:%4d,min:%4d\r\n",voltage_max,voltage_min);
            printf("pir_check\r\n");
            voltage_max = PIR_ADC_BASE_MAX;
            voltage_min = PIR_ADC_BASE_MIN;

            period_time = pir_info.wander_time - (pir_info.wander_time / pir_info.wander_num);
            //当前时间大于最后时间段的起点，
            if((cur_time >= period_time) && (cur_time <= pir_info.wander_time))
            {
                pir_running = 2;
            }
        }

        

        //时间已到，不满足条件
        if((cur_time >= pir_info.wander_time) && (pir_running != 2))
        {
            printf("Pir_Clear\r\n");
            pir_wakeup_interval = KOS_PARAM_WAKE_INTERVAL;
            OSAL_SetWakeupInterval(pir_wakeup_interval);
            Pir_ClearValue();
        }

        //满足条件 徘徊报警触发
        if(pir_running == 2)      
        {
            // printf("Pir_in\r\n");
            pir_wakeup_interval = KOS_PARAM_WAKE_INTERVAL;
            OSAL_SetWakeupInterval(pir_wakeup_interval);
            Pir_ClearValue();
            #ifdef PIR_TEST_WITHOUT_INTERVAL
            timeout_timestamp = PIR_TIMEOUT_SEC_TIME;
            #else
            OSAL_TimeGet(&timeout_timestamp, T_UTC);        //记录当前时间戳
            #endif 
            return 1;
        }
    }

    //PIR触发初步过滤，满足300ms
    if((irq_wakeup_cnt >= 2) && (adc_flag) && (pir_running == 0))    
    {
        pir_wakeup_interval = PIR_PARAM_WAKE_INTERVAL;
        OSAL_SetWakeupInterval(pir_wakeup_interval);            //修改定时唤醒为50ms
        printf("Pir_start \r\n");
        irq_wakeup_cnt = 0;
        adc_flag = 0;
        pir_running = 1;
        // return 1;   
    }

    return 0;
}


/**
 * @brief: 低功耗定时器回调函数
 * @note: 
 * @param {uint32_t} dev
 * @return 
 */
static int32_t Pir_TimedWakeupHandle(uint32_t dev)
{
    int32_t ret = 0;
    uint32_t cur_rtc = 0;
    OSAL_TimeGet(&cur_rtc, T_UTC);

    // timeout_timestamp += pir_wakeup_interval;

    if((pir_info.enable) && ((cur_rtc-timeout_timestamp) >= PIR_TIMEOUT_SEC_TIME) )   //满足5分钟，才会再次检测
    {
        // printf("pir_tim\r\n");
        ret = Pir_TimeWakeupProcess();
    }

    return ret;
}
COMPONENT_WAKEUP_EXPORT(COMP_PIR, Pir_TimedWakeupHandle, vTIMER_1); 